package com.cocolover2.andbase.caches;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;

import com.cocolover2.andbase.utils.encrypt.EncryptUtil;
import com.jakewharton.disklrucache.DiskLruCache;
import com.jakewharton.disklrucache.Util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

/**
 * 磁盘缓存数据(最大100M)
 * Created by cocolove2 on 17/8/15.
 */

class DiskCacheImpl implements CocoCaches {

    private static final String DIR_NAME = "diskCache_data";
    private static final int MAX_COUNT = 100 * 1024 * 1024;
    private static final int DEFAULT_APP_VERSION = 1;

    private DiskLruCache mDiskLruCache;
    private String mDirName;


    DiskCacheImpl(Context context, String dirName) throws IOException {
        if (TextUtils.isEmpty(dirName)) {
            dirName = DIR_NAME;
        }
        mDiskLruCache = generateCache(context, dirName, MAX_COUNT);
    }

    public String getCacheDir() {
        if (mDiskLruCache != null) {
            return mDiskLruCache.getDirectory().getAbsolutePath();
        } else {
            return "";
        }
    }

    String getDirName() {
        return mDirName;
    }


    private DiskLruCache generateCache(Context context, String dirName, int maxCount) throws IOException {
        mDirName = dirName;
        int versionCode = 1;
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            versionCode = info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            versionCode = 1;
        }
        return DiskLruCache.open(getDiskCacheDir(context, dirName), versionCode, DEFAULT_APP_VERSION, maxCount);
    }


    @Override
    public void saveToCache(String key, Object value) {
        put(key, (Serializable) value);
    }

    void saveToCache(String key, byte[] value) {
        put(key, value);
    }

    @Override
    public Object getFromCache(String key, Object defaultObject) {
        Object object = getAsSerializable(key);
        if (object == null) {
            object = defaultObject;
        }
        return object;
    }

    @Override
    public void removeFromCache(String key) {
        remove(key);
    }

    @Override
    public void clearCache() {
        deleteDiskCache();
    }


    private File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath = "";
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            if (context.getExternalCacheDir() != null) {
                cachePath = context.getExternalCacheDir().getPath();
            }
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }


    private DiskLruCache.Editor editor(String key) {
        try {
            //wirte DIRTY
            DiskLruCache.Editor edit = mDiskLruCache.edit(buildKey(key));
            //edit maybe null :the entry is editing
            if (edit != null) {
                return edit;
            }

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

        return null;
    }


    private InputStream get(String key) {
        try {
            DiskLruCache.Snapshot snapshot = mDiskLruCache.get(buildKey(key));
            if (snapshot == null) { //not find entry , or entry.readable = false
                return null;
            }
            //write READ
            return snapshot.getInputStream(0);

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private String buildKey(String orignalKey) {
        return EncryptUtil.enctryMD5(orignalKey);
    }


    /**
     * 保存 byte数据 到 缓存中
     *
     * @param key   保存的key
     * @param value 保存的数据
     */
    private void put(String key, byte[] value) {
        OutputStream out = null;
        DiskLruCache.Editor editor = null;
        try {
            editor = editor(key);
            if (editor == null) {
                return;
            }
            out = editor.newOutputStream(0);
            out.write(value);
            out.flush();
            editor.commit();//write CLEAN
            flush();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                if (editor != null) {
                    editor.abort();//write REMOVE
                }
                flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } finally {
            Util.closeQuietly(out);
        }
    }


    byte[] getAsBytes(String key) {
        byte[] res = null;
        InputStream is = get(key);
        if (is == null) {
            return null;
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = is.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            res = baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Util.closeQuietly(baos);
            Util.closeQuietly(is);
        }
        return res;
    }


    private void put(String key, Serializable value) {
        DiskLruCache.Editor editor = editor(key);
        ObjectOutputStream oos = null;
        if (editor == null) {
            return;
        }
        try {
            OutputStream os = editor.newOutputStream(0);
            oos = new ObjectOutputStream(os);
            oos.writeObject(value);
            oos.flush();
            editor.commit();
            flush();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                editor.abort();
                flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } finally {
            Util.closeQuietly(oos);
        }
    }

    @SuppressWarnings("all")
    private <T> T getAsSerializable(String key) {
        T t = null;
        InputStream is = get(key);
        ObjectInputStream ois = null;
        if (is == null) {
            return null;
        }
        try {
            ois = new ObjectInputStream(is);
            t = (T) ois.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Util.closeQuietly(ois);
        }
        return t;
    }


    private boolean remove(String key) {
        try {
            return mDiskLruCache.remove(buildKey(key));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 将所有的缓存数据全部删除
     */
    private void deleteDiskCache() {
        try {
            mDiskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 将缓存记录同步到journal文件中。<br>
     * 当你保存完毕后最好调用下该方法。<br>
     */
    private void flush() {
        try {
            mDiskLruCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
